package br.com.iswe.teste.web;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.SessionFactory;
import org.springframework.security.AuthenticationManager;
import org.xml.sax.SAXException;

import br.com.iswe.teste.acegi.ACEGITestCaseHelper;
import br.com.iswe.teste.dados.TestDataManager;
import br.com.iswe.teste.hibernate.HibernateTestCaseHelper;
import br.com.iswe.teste.spring.SpringTestCase;

import com.meterware.httpunit.WebConversation;
/**
 * Classe Base para Test Cases Web utilizando HttpUnit.
 * 
 * @author Rafael Noronha
 *
 */
@SuppressWarnings("unchecked")
public abstract class HttpTestCase extends SpringTestCase {
	
	private BasicTesteSuite basicTesteSuite = null;
	public HttpTestCase(String metodoTest, BasicTesteSuite basicTesteSuite) {
		super(metodoTest);
		this.basicTesteSuite = basicTesteSuite;
	}

    private static ThreadLocal<HttpTestCaseExecutionInfo> executionInfo = new ThreadLocal<HttpTestCaseExecutionInfo>();
    
    protected HttpTestCaseExecutionInfo getExecInfo(){
        if(executionInfo.get() == null){
            executionInfo.set(new HttpTestCaseExecutionInfo());
        }
        return executionInfo.get();
    }
    
	//Guarda os tempos de execucao dos testes por classe de test case
	protected static Map<Class, Map<String, List<Long>>> timeResults = new HashMap<Class, Map<String, List<Long>>>();
	
	@Override
	protected void setUp() throws Exception {
		super.setUp();
		// ###### configura os dados de teste ###### //
		if(!getExecInfo().isSetuped()){
            getExecInfo().setSetuped( true );
			
			AutenticationInfo autenticationInfo = basicTesteSuite.getAutenticationInfo(); 
			
			//Verifica se possui informacao de Autenticacao.
			if(autenticationInfo != null){
				String user = autenticationInfo.getUserName();
				String password = autenticationInfo.getPassWord();
			
				//Obtem a session factory de ISWEPerfil
				SessionFactory sessionFactory = null;
				if(getApplicationContext().containsBean(autenticationInfo.getSessionFactoryName())){
					sessionFactory = (SessionFactory)getApplicationContext().getBean(autenticationInfo.getSessionFactoryName()); 		
					//Aplica o Pattern OpenSessioninView para manter a sessao do hibernate entre as chamadas do teste
					HibernateTestCaseHelper.initRequest(sessionFactory);
				}
				String acegiAuthenticationManager = autenticationInfo.getAuthenticationManager();
				if(getApplicationContext().containsBean(acegiAuthenticationManager)){
					//Efetua o login de um usuario realizar os testes 
					AuthenticationManager authenticationManager = (AuthenticationManager)getApplicationContext().getBean(acegiAuthenticationManager);
					ACEGITestCaseHelper.doLogin(authenticationManager, user, password);
				}
				if(sessionFactory != null){
					HibernateTestCaseHelper.endRequest(sessionFactory);
				}
			}
			
			try{
				// -- Configura os Objetos para o Teste Web -- //
				//WebConversation
                getExecInfo().setWc( new WebConversation() );
				//Response
				//Efetua o login web na aplicacao
                String usuario = "httpUnit-" + getThreadNum();
                getExecInfo().setResp( getExecInfo().getWc().getResponse( TestDataManager.getInstance(this.getClass()).getUrlHost() + "doLogin.iswe?ISWE_LOGIN_REQUEST_BEAN_NAME=loginWBean&login=" + usuario + "&senha=123" ));
				//if(autenticationInfo != null && autenticationInfo.isLoginWebEnabled()){
                //    getExecInfo().setResp( getExecInfo().getWc().getResponse( "http://localhost:8080/ISWEPerfil/doLogin.iswe?ISWE_LOGIN_REQUEST_BEAN_NAME=loginWBean&login=" + autenticationInfo.getUserName() + "&senha=" + autenticationInfo.getPassWord() ));//Efetuar o login.
				//}
				//obtem a pagina inicial.
                getExecInfo().setResp( getExecInfo().getWc().getResponse( TestDataManager.getInstance(this.getClass()).getUrlHost() + getUrl() ));//Navega para a Url de Inicio.
			}
			catch(MalformedURLException exp){
				log.error("Problemas ao criar WebResponse, A URL informada esta incorreta : " + getUrl(), exp);
				throw exp;
			}
			catch(IOException exp){
				log.error("Problemas ao criar WebResponse, IOException ", exp);
				throw exp;
			}
			catch(SAXException exp){
				log.error("Problemas ao criar WebResponse, SAXException ", exp);
				throw exp;
			}
            getExecInfo().setFinished( false );
		}
	}
	
	
    /**
     * M�todo que retorna o numero da thread atual por caso de uso.
     * @return
     */
    protected Integer getThreadNum(){
    	HttpTestUserThreadRunner thread = (HttpTestUserThreadRunner)Thread.currentThread();
        return thread.getThreadNum();
    }
    
    @Override
	protected void tearDown() throws Exception {
		super.tearDown();
		if(getExecInfo().isFinished()){
			//Indica que o caso de teste foi finalizado.
            getExecInfo().setSetuped( false );
		}
	}
	
	protected void runTest() throws Throwable {
		assertNotNull(getName());
		Method runMethod= null;
		try {
			// use getMethod to get all public inherited
			// methods. getDeclaredMethods returns all
			// methods of this class but excludes the
			// inherited ones.
			runMethod = getClass().getMethod(getName());
		} catch (NoSuchMethodException e) {
			fail("Method \"" + getName() + "\" not found");
		}
		if (!Modifier.isPublic(runMethod.getModifiers())) {
			fail("Method \"" + getName() + "\" should be public");
		}

		try {
			//Obtem o tempo de execucao do metodo
			long initTime = System.currentTimeMillis();
			runMethod.invoke(this);
			long totalTime = System.currentTimeMillis() - initTime;
			//Grava esse resultado.
			addTimeResults(getName(), totalTime);
		}
		catch (InvocationTargetException e) {
			e.fillInStackTrace();
			throw e.getTargetException();
		}
		catch (IllegalAccessException e) {
			e.fillInStackTrace();
			throw e;
		}
	}
	
	/**
	 * Este metodo verifica se a execao � devida a algum bug
	 * q nao influencia no bom comportamento da aplicacao ou � realmente um erro.
	 */
	@Deprecated
	protected void checkBugException(Throwable exp) {
		String [] textoComparar = new String[]{"org.mozilla.javascript.EcmaError: TypeError: Cannot find function scrollTo",
				"org.mozilla.javascript.EcmaError: ReferenceError: \"jscalendarSetImageDirectory\" is not defined."};
		
		String mensagem = exp.getMessage();
		for (String texto : textoComparar) {
			if(mensagem.indexOf(texto) > -1){
				return;//� um bug dos js do faces.. nao deve lancar pra cima a execao
			}
		}
		if(exp instanceof RuntimeException){
			throw (RuntimeException)exp; 
		}
		else{
			throw new RuntimeException(exp);
		}
	}
	
	protected abstract String getUrl();
	
	protected void addTimeResults(String nomeMetodo, Long valorMilisegundos){
		//Pega o mapa de resultados pela classe
		Map<String, List<Long>> timeByClass = timeResults.get(getClass());
		//verifica se esta nulo
		if(timeByClass == null){
			timeByClass = new HashMap<String, List<Long>> ();
			timeResults.put(getClass(), timeByClass);
		}
		//verifica se ja existe algum dado para esse metodo
		List<Long> times = timeByClass.get(nomeMetodo);
		if(times == null){
			times = new ArrayList<Long>();
		}
		times.add(valorMilisegundos);
		timeByClass.put(nomeMetodo, times);
	}
	
	public Map<String, List<Long>> getTimeResults() {
		return timeResults.get(getClass());
	}
}